/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       water.h
 * Author:     karooolek
 * Created on: Dec 18, 2009
 *
 **********************************************************************************************************************/

#ifndef WATER_H_
#define WATER_H_

#include "scenenode.h"

namespace mGameEngine
{
namespace Game
{

/**
 * Water.
 * Water is simulated using Gerstner waves model.
 * I
 */
class Water : public SceneNode, public Graphics::Drawable
{
protected:
    /**
     * Timer used for simulation.
     */
    Timer _timer;

    /**
     * Waves amplitudes.
     */
    Vector4 _amplitude;

    /**
     * Waves lengths.
     */
    Vector4 _length;

    /**
     * Waves steepness.
     */
    Vector4 _steepness;

    /**
     * Waves velocities.
     */
    Matrix4x4 _velocity;

    /**
     * Water color.
     */
    Color _color;

    /**
     * Fresnel effect parameters.
     * x - bias
     * y - scale
     * z - power
     */
    Vector3 _fresnel;

    /**
     * Environment map.
     */
    const Graphics::Texture *_envMap;

    /**
     * Environment color.
     */
    Color _envColor;

    /**
     * Wavelets normal map.
     */
    const Graphics::Texture *_normalMap;

    /**
     * Wavelets normal map size.
     */
    Vector2 _normalMapSize;

    /**
     * Wavelets normal map scale.
     */
    float _normalMapScale;

    /**
     * Foam texture.
     */
    const Graphics::Texture *_foam;

    /**
     * Foam texture size.
     */
    Vector2 _foamSize;

    /**
     * Foam parameters.
     * x - minimal foam height
     * y - maximal foam height
     */
    Vector2 _foamMinMax;

    /**
     * Calculate Gerstner wave displacement at position.
     * @param position position.
     * @return Gerstner wave displacement.
     */
    Vector3 _calcGerstner(const Vector2 &position) const;

public:
    /**
     * Create water.
     */
    Water();

    /**
     * Destroy water.
     */
    virtual ~Water();

    /**
     * Get specified wave ampltiude.
     * @param wave wave number.
     * @return specified wave amplitude.
     */
    float getWaveAmplitude(uint8 wave) const
    {
        return _amplitude.v[wave];
    }

    /**
     * Set specified wave amplitude.
     * @param wave wave number.
     * @param amplitude amplitude.
     */
    void setWaveAmplitude(uint8 wave, float amplitude)
    {
        if(amplitude < 0.0f)
        {
            return;
        }

        _amplitude.v[wave] = amplitude;
    }

    /**
     * Get specified wave length.
     * @param wave wave number.
     * @return specified wave length.
     */
    float getWaveLength(uint8 wave) const
    {
        return _length.v[wave];
    }

    /**
     * Set specified wave length.
     * @param wave wave number.
     * @param length length.
     */
    void setWaveLength(uint8 wave, float length)
    {
        if(length <= 0.0f)
        {
            return;
        }

        _length.v[wave] = length;
    }

    /**
     * Get specified wave steepness.
     * @param wave wave number.
     * @return specified wave steepness.
     */
    float getWaveSteepness(uint8 wave) const
    {
        return _steepness.v[wave];
    }

    /**
     * Set specified wave steepness.
     * @param wave wave number.
     * @param steepness steepness from 0.0 to 1.0.
     */
    void setWaveSteepness(uint8 wave, float steepness)
    {
        if(steepness < 0.0f || steepness > 1.0f)
        {
            return;
        }

        _steepness.v[wave] = steepness;
    }

    /**
     * Get specified wave velocity.
     * @param wave wave number.
     * @return specified wave velocity.
     */
    Vector2 getWaveVelocity(uint8 wave) const
    {
        return Vector2(_velocity.m[wave]);
    }

    /**
     * Set specified wave velocity.
     * @param wave wave number.
     * @param velocity velocity.
     */
    void setWaveVelocity(uint8 wave, const Vector2 &velocity)
    {
        _velocity.m[wave][0] = velocity.x;
        _velocity.m[wave][1] = velocity.y;
    }

    /**
     * Set specified wave parameters.
     * @param wave wave number.
     * @param amplitude wave amplitude.
     * @param length wave length (peek to peek)
     * @param steepness wave steepness between 0 and 1.
     * @param velocity wave velocity.
     */
    void setWaveParameters(uint8 wave,
                            float amplitude, float length, float steepness,
                            const Vector2 &velocity)
    {
        setWaveAmplitude(wave, amplitude);
        setWaveLength(wave, length);
        setWaveSteepness(wave, steepness);
        setWaveVelocity(wave, velocity);
    }

    /**
     * Get water color.
     * @return water color.
     */
    const Color &getColor() const
    {
        return _color;
    }

    /**
     * Set water color.
     * @param color color.
     */
    void setColor(const Color &color)
    {
        _color = color;
    }

    /**
     * Get Fresnel effect scale.
     * @return Fresnel effect scale.
     */
    float getFresnelScale() const
    {
        return _fresnel.x;
    }

    /**
     * Set Fresnel effect scale.
     * @param scale Fresnel effect scale.
     */
    void setFresnelScale(float scale)
    {
        _fresnel.x = scale;
    }

    /**
     * Get Fresnel effect bias.
     * @return Fresnel effect bias.
     */
    float getFresnelBias() const
    {
        return _fresnel.y;
    }

    /**
     * Set Fresnel effect bias.
     * @param bias Fresnel effect bias.
     */
    void setFresnelBias(float bias)
    {
        _fresnel.y = bias;
    }

    /**
     * Get Fresnel effect power.
     * @return Fresnel effect power.
     */
    float getFresnelPower() const
    {
        return _fresnel.z;
    }

    /**
     * Set Fresnel effect power.
     * @param power Fresnel effect power.
     */
    void setFresnelPower(float power)
    {
        _fresnel.z = power;
    }

    /**
     * Set Fresnel effect parameters.
     * @param scale scale.
     * @param bias bias.
     * @param power power.
     */
    void setFresnelParameters(float scale, float bias, float power)
    {
        setFresnelScale(scale);
        setFresnelBias(bias);
        setFresnelPower(power);
    }

    /**
     * Get water environment (reflection) map.
     * @return environment (reflection) map.
     */
    const Graphics::Texture *getEnvironmentMap() const
    {
        return _envMap;
    }

    /**
     * Set water environment (reflection) map.
     * @param envMap environment map.
     */
    void setEnvironmentMap(const Graphics::Texture *envMap)
    {
        _envMap = envMap;
    }

    /**
     * Get environment color.
     * @return environment color.
     */
    const Color &getEnvironmentColor() const
    {
        return _envColor;
    }

    /**
     * Set environement color.
     * @param color environment color.
     */
    void setEnvironmentColor(const Color &envColor)
    {
        _envColor = envColor;
    }

    /**
     * Get wavelets normal map.
     * @return wavelets normal map.
     */
    const Graphics::Texture *getNormalMap() const
    {
        return _normalMap;
    }

    /**
     * Set wavelets normal map.
     * @param normalMap normal map.
     */
    void setNormalMap(const Graphics::Texture *normalMap)
    {
        _normalMap = normalMap;
    }

    /**
     * Get wavelets normal map size.
     * @return wavelets normal map size.
     */
    const Vector2 &getNormalMapSize() const
    {
        return _normalMapSize;
    }

    /**
     * Set wavelets normal map size.
     * @param normalSize wavelets normal map size.
     */
    void setNormalMapSize(const Vector2 &normalSize)
    {
        _normalMapSize = normalSize;
    }

    /**
     * Get wavelets normal map scale.
     * @return wavelets normal map scale.
     */
    float getNormalMapScale() const
    {
        return _normalMapScale;
    }

    /**
     * Set wavelets normal map scale.
     * @param normalScale normal map scale.
     */
    void setNormalMapScale(float normalScale)
    {
        if(normalScale < 0.0f)
        {
            return;
        }

        _normalMapScale = normalScale;
    }

    /**
     * Get foam texture.
     * @return foam texture.
     */
    const Graphics::Texture *getFoamTexture() const
    {
        return _foam;
    }

    /**
     * Set foam texture.
     * @param foam foam texture.
     */
    void setFoamTexture(const Graphics::Texture *foam)
    {
        _foam = foam;
    }

    /**
     * Get foam texture size.
     * @return foam texture size.
     */
    const Vector2 &getFoamTextureSize() const
    {
        return _foamSize;
    }

    /**
     * Set foam texture size.
     * @param foamSize foam texture size.
     */
    void setFoamTextureSize(const Vector2 &foamSize)
    {
        _foamSize = foamSize;
    }

    /**
     * Get minimal height where foam is visible.
     * @return minimal height.
     */
    float getFoamMinimalHeight() const
    {
        return _foamMinMax.x;
    }

    /**
     * Set minimal height where foam is visible.
     * @param minHeight minimal height.
     */
    void setFoamMinimalHeight(float minHeight)
    {
        _foamMinMax.x = minHeight;
    }

    /**
     * Get maximal height where foam is visible.
     * @return maximal height.
     */
    float getFoamMaximalHeight() const
    {
        return _foamMinMax.y;
    }

    /**
     * Set maximal height where foam is visible.
     * @param maxHeight maximal height.
     */
    void setFoamMaximalHeight(float maxHeight)
    {
        _foamMinMax.y = maxHeight;
    }

    /**
     * Set foam parameters.
     * @param minHeight minimal height.
     * @param maxHeight maximal height.
     */
    void setFoamParameters(float minHeight, float maxHeight)
    {
        setFoamMinimalHeight(minHeight);
        setFoamMaximalHeight(maxHeight);
    }

    /**
     * Draw water.
     */
    virtual void draw() const;

    /**
     * Get water height at specified position.
     * @param position position.
     * @return water height at position.
     */
    virtual float getHeightAt(const Vector2 &position) const;

    /**
     * Get water normal at specified position.
     * @param position position.
     * @return water normal at position.
     */
    virtual Vector3 getNormalAt(const Vector2 &position) const;

    /**
     * Get water slope at specified position.
     * @param position position.
     * @return water slope in radians at position.
     */
    virtual float getSlopeAt(const Vector2 &position) const;
};

}
}

#endif // WATER_H_
